package lk.service;


import lk.dao.FileDao;
import lk.exception.ResponseCodeContants;
import lk.exception.RestfulRecord;
import lk.pojo.FileResource;
import lk.util.FtpClientFactory;
import lk.util.FtpClientPool;
import lk.util.FtpUtil;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.util.Properties;
import java.util.UUID;

/**
 * 文件操作业务层实现类
 *
 */
@Service
public class FileServiceImpl implements FileService {

    /**
     * 文件上传类型，默认存服务器
     * 0、随前端；1、入库；2、存服务器；3、存 FTP
     */
    private static char UPLOAD_TYPE;

    /**
     * 服务器及 FTP 方式存取的文件夹前缀
     */
    private static String FILE_PREFIX_PATH;

    @Resource
    private FileDao fileDao;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public RestfulRecord upload(MultipartFile file, Character uploadType) throws Exception {
        uploadType = getUploadType(uploadType);
        byte[] bytes;
        try {
            bytes = file.getBytes();
        } catch (IOException e) {
            e.printStackTrace();
            return new RestfulRecord(ResponseCodeContants.ERROR_CODE, "文件上传失败");
        }
        String fileId = UUID.randomUUID().toString().replaceAll("-", "");
        long fileSize = file.getSize();

        FileResource fileResource = new FileResource();
        fileResource.setFileId(fileId);
        fileResource.setFileName(file.getOriginalFilename());
        fileResource.setUploadType(uploadType);
        fileResource.setFileType(getExtensionName(file.getOriginalFilename()));
        fileResource.setFileSize(fileSize);
        switch (uploadType) {
            case FILE_UPLOAD_ONE:
                if (fileSize > FILE_MAX_SIZE) {
                    return new RestfulRecord(ResponseCodeContants.ERROR_CODE, "入库文件大小不能超过 100 kb");
                }
                String base64Str = FileService.BASE_64_ENCODER.encode(bytes);
                fileResource.setFilePath(null);
                fileResource.setFileForChar(base64Str);
                fileDao.addFile(fileResource);
                break;
            case FILE_UPLOAD_TWO:
                // 先存数据库，再存服务器，存服务器异常时可回滚数据库数据
                StringBuffer stringBuffer = new StringBuffer();
                String filePath = stringBuffer.append(FILE_PREFIX_PATH).append(File.separator).append(fileId).append(File.separator).append(file.getOriginalFilename()).toString();
                fileResource.setFilePath(filePath);
                fileResource.setFileForChar(null);
                fileDao.addFile(fileResource);
                uploadFileToService(bytes, filePath);
                break;
            case FILE_UPLOAD_THREE:
                String filePath2 = FtpUtil.FTP_BASE_PATH + File.separator + fileId;
                FTPClient ftp = FtpClientPool.borrowObject();
                FtpUtil.upload(file.getInputStream(), filePath2, file.getOriginalFilename(), ftp);
                FtpClientPool.returnObject(ftp);
                fileResource.setFilePath(filePath2);
                fileResource.setFileForChar(null);
                fileDao.addFile(fileResource);
                break;
            default:
                return new RestfulRecord(ResponseCodeContants.ERROR_CODE, "不支持的上传类型:" + uploadType);
        }
        return new RestfulRecord(200,"上传成功", fileResource);
    }

    @Override
    public void download(String fileId, HttpServletResponse response) throws Exception {
        FileResource fileResource = fileDao.findFileById(fileId);
        byte[] file;
        if (fileResource.getUploadType().equals(FILE_UPLOAD_ONE)) {
            file = FileService.BASE_64_DECODER.decodeBuffer(fileResource.getFileForChar());
        } else if (fileResource.getUploadType().equals(FILE_UPLOAD_TWO)) {
            file = getFileFromService(fileResource.getFilePath());
        } else {
            setFileResponse(response, fileResource.getFileName());
            FTPClient ftp = FtpClientPool.borrowObject();
            FtpUtil.downloadFiles(response.getOutputStream(), fileResource.getFilePath(), fileResource.getFileName(), ftp);
            FtpClientPool.returnObject(ftp);
            return;
        }
        if (file == null) {
            throw new NullPointerException();
        }
        try (OutputStream toClient = new BufferedOutputStream(response.getOutputStream())) {
            setFileResponse(response, fileResource.getFileName());
            toClient.write(file);
            toClient.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     *  文件下载的响应类型及文件名称设置
     * @param response 响应
     * @param fileName 文件名
     */
    private static void setFileResponse(HttpServletResponse response, String fileName) {
        try {
            response.reset();
            response.setContentType("application/octet-stream");
            response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }

    /**
     * 文件上传相关参数初始化
     */
    @PostConstruct
    public void initialize() {
        System.out.println("文件上传的配置文件加载");
        Properties pro = new Properties();
        try {
            InputStreamReader in = new InputStreamReader(this.getClass().getResourceAsStream("/fileUtil.properties"), FtpUtil.LOCAL_CHARSET);
            pro.load(in);
            in.close();
            UPLOAD_TYPE = pro.getProperty(FILE_UPLOAD_TYPE_NAME).charAt(0);
            FILE_PREFIX_PATH = pro.getProperty(FILE_SERVICE_PATH_NAME);
            if(UPLOAD_TYPE == FILE_UPLOAD_THREE){
                if(pro.getProperty(FILE_ADDRESS_NAME) != null){
                    FtpUtil.FTP_ADDRESS = pro.getProperty(FILE_ADDRESS_NAME);
                }
                if(pro.getProperty(FILE_PORT_NAME) != null ){
                    FtpUtil.FTP_PORT = Integer.parseInt(pro.getProperty(FILE_PORT_NAME));
                }
                if(pro.getProperty(FILE_USERNAME_NAME) != null){
                    FtpUtil.FTP_USERNAME = pro.getProperty(FILE_USERNAME_NAME);
                }
                if(pro.getProperty(FILE_PASSWORD_NAME) != null){
                    FtpUtil.FTP_PASSWORD = pro.getProperty(FILE_PASSWORD_NAME);
                }
                if(pro.getProperty(FTP_BASE_PATH_NAME) != null){
                    FtpUtil.FTP_BASE_PATH = pro.getProperty(FTP_BASE_PATH_NAME);
                }
                if(pro.getProperty(FTP_TIMEOUT_NAME) != null){
                    FtpUtil.FTP_TIME_OUT = Integer.parseInt(pro.getProperty(FTP_TIMEOUT_NAME));
                }
                if(pro.getProperty(FTP_BUFFER_SIZE_NAME) != null){
                    FtpUtil.FTP_BUFFER_SIZE = Integer.parseInt(pro.getProperty(FTP_BUFFER_SIZE_NAME));
                }
                GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
                poolConfig.setMaxTotal(pro.getProperty(FTP_MAX_TOTAL_NAME) == null ? 50 : Integer.parseInt(pro.getProperty(FTP_MAX_TOTAL_NAME)));
                poolConfig.setMinIdle(pro.getProperty(FTP_MIN_IDLE_NAME) == null ? 10 : Integer.parseInt(pro.getProperty(FTP_MIN_IDLE_NAME)));
                poolConfig.setMaxIdle(pro.getProperty(FTP_MAX_IDLE_NAME) == null ? 100 : Integer.parseInt(pro.getProperty(FTP_MAX_IDLE_NAME)));
                FtpClientPool.FTP_CLIENT_POOL = new GenericObjectPool<>(new FtpClientFactory(), poolConfig);
            }
        }catch (NullPointerException e){
            System.out.println("文件上传的配置文件不存在");
        }catch (IOException e){
            e.printStackTrace();
        }
    }

    /**
     * 辅助方法：获取最终的文件上传类型，默认数据库上传
     *
     * @param uploadType 前端传的文件上传类型
     * @return 文件上传类型
     */
    private char getUploadType(Character uploadType) {
        if (UPLOAD_TYPE != 0) {
            return UPLOAD_TYPE;
        }
        return uploadType == null ? FILE_UPLOAD_TWO : uploadType;
    }

    /**
     * 辅助方法：上传文件到服务器
     *
     * @param bytes    文件
     * @param filePath 文件路径
     */
    private static void uploadFileToService(byte[] bytes, String filePath) {
        File file = new File(filePath);
        if (!file.getParentFile().exists() && !file.getParentFile().mkdirs()) {
            System.out.println("文件夹创建失败:" + file.getParentFile());
            return;
        }
        try (BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(file))) {
            out.write(bytes);
            out.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 辅助方法：从服务器下载文件
     *
     * @param filePath 文件路径
     * @return 文件
     */
    private byte[] getFileFromService(String filePath) {
        File file = new File(filePath);
        try (BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream(file))) {
            byte[] b = new byte[inputStream.available()];
            int count = inputStream.read(b);
            System.out.println("count = " + count);
            return b;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 获取文件扩展名
     *
     */
    public static String getExtensionName(String filename) {
        if ((filename != null) && (filename.length() > 0)) {
            int dot = filename.lastIndexOf('.');
            if ((dot >-1) && (dot < (filename.length() - 1))) {
                return filename.substring(dot + 1);
            }
        }
        return filename;
    }
}
